'use strict';

var ctrl = module.exports = {};
var debug = require('debug'),
  log = debug('yozh:mo:user:log'),
  error = debug('yozh:mo:user:error'),
  verbose = debug('yozh:mo:user:verbose'),
  uuid = require('uuid'),
  service = require('./user.service'),
  roleService = require('../role/role.service'),
  koajwt = require('koa-jwt'),
  tools = require('../../lib/tools'),
  config = require('../../config/config'),
  tokenCache = require('../../lib/cache')(config.tokenCache),
  errorsCache = require('../../lib/cache')(config.errorsCache),
  limitCache = require('../../lib/cache')(config.limitCache),
  respData = require('../../config/json_resp_data'),
  httpRequest = require('../../config/http_request'),
  myErrCode = require('../../config/my_err_code'),
  validators = require('../../lib/validators')
  ;

var apiVersion = 'v1/',
  formTokenName = 'formtoken',
  formTokenType = 'formToken'
  ;

/**
 * create a form token cache with ip, timestamp, livetime
 * @param {Function} next          [description]
 * @yield {[type]}   [description]
 */
ctrl.createFormToken = function *(next) {
  // this.state.skipChectToken = true;
  var ip = tools.getClientIp(this.request);
  var date = new Date();
  log('client ip: ', ip, 'create time: ', date.toString());
  var token = koajwt.sign({
    timestamp: date.getTime(),
    type: config.constants.formToken
  }, config.secret);

  var links = respData.createSimpleLinks(this.path);
  // set token to redis with ip and time
  if (token) {
    var setTokenRes = yield tokenCache.set(token, JSON.stringify({clientIp: ip, createTime: date.toString()}));
    if (setTokenRes === 1) {
      setTokenRes = yield tokenCache.expire(token, tokenCache.defaultExpireTime);
    }
    var datas = respData.createSimpleData(config.constants.formToken, token);

    this.status = 201;
    this.body = JSON.stringify({links: links, data: datas});
    return yield next;
  }
  var errors = respData.createErrors(
    500,
    '01009',
    '生成表单token失败',
    config.constants.formToken,
    {
      method: this.request.method,
      path: this.path
    }
  );
  var setErrorRes = yield errorsCache.set(errors.id, JSON.stringify(errors));
  if (setErrorRes === 1) {
    setErrorRes = errorsCache.expire(errors.id, errorsCache.defaultExpireTime);
  }

  this.status = 500;
  this.body = JSON.stringify({links: links, errors: errors});
  return yield next;
};

ctrl.createAuthorization = function *(next) {
  // var params = this.request.body;
  var links = respData.createSimpleLinks(this.path);
  var errors = validators.check(this, [{
    // param: 'token',
    'token': [{
      checkFun: 'need',
      errStatus: 401,
      errCode: '01001',
      errDetail: '缺少表单token参数',
      errSource: 'createAuthorization'
    }]
  }, {
    'userName': [{
      checkFun: 'need',
      errStatus: 400,
      errCode: '01002',
      errDetail: '缺少表单userName参数',
      errSource: 'createAuthorization'
    }, {
      checkFun: 'isUsername',
      errStatus: 400,
      errCode: '01003',
      errDetail: 'userName必须是以字母开头的6到20位字符串',
      errSource: 'createAuthorization'
    }]
  }, {
    'passWord': [{
      checkFun: 'need',
      errStatus: 400,
      errCode: '01002',
      errDetail: '缺少表单passWord参数',
      errSource: 'createAuthorization'
    }, {
      checkFun: 'isPassword',
      errStatus: 400,
      errCode: '01003',
      errDetail: 'passWord必须是6到20位字符串',
      errSource: 'createAuthorization'
    }]
  }], 'createAuthorization');
  if (errors) {
    log(errors.source, errors.detail);
    var setErrorRes = yield errorsCache.set(errors.id, JSON.stringify(errors));
    if (setErrorRes === 1) {
      setErrorRes = errorsCache.expire(errors.id, errorsCache.defaultExpireTime);
    }
    this.status = errors.status;
    this.body = JSON.stringify({links: links, errors: errors});
    return yield next;
  }
  var body = this.request.body;
  // check token in redis
  var getTokenRes = yield tokenCache.get(body.token);
  if (getTokenRes === null || getTokenRes !== body.token) {
    var checkTokenErrors = respData.createErrors(
      401,
      '01001',
      'token不存在或者页面超时',
      config.constants.formToken,
      {
        method: this.request.method,
        path: this.path
      }
    );
    var setTERes = yield errorsCache.set(checkTokenErrors.id, JSON.stringify(checkTokenErrors));
    if (setTERes === 1) {
      setTERes = errorsCache.expire(checkTokenErrors.id, errorsCache.defaultExpireTime);
    }

    this.status = 401;
    this.body = JSON.stringify({links: links, errors: checkTokenErrors});
    return yield next;
  }
  // check user account and password in mongodb;
  var user = yield service.checkUserLogin({
    userName: body.userName,
    passWord: body.passWord
  });
  log('request body: ', body, 'user: ', user);
  if (user) {
    var token = koajwt.sign({
      userId: user._id,
      userName: user.userName,
      roleId: user.roleId._id
    }, config.secret);
    var setTokenRes = yield tokenCache.set(token, '1');
    if (setTokenRes === 1) {
      setTokenRes = yield tokenCache.expire(token, tokenCache.defaultExpireTime);
    }
    var datas = respData.createSimpleData(config.constants.authorization, token);

    this.status = 201;
    this.body = JSON.stringify({links: links, data: datas});
    return yield next;
  }
  // can not find user with account and password
  var cerrors = respData.createErrors(
    400,
    '02002',
    '账号密码不匹配',
    config.constants.formToken,
    {
      method: this.request.method,
      path: this.path
    }
  );
  var setCERes = yield errorsCache.set(cerrors.id, JSON.stringify(cerrors));
  if (setCERes === 1) {
    setCERes = errorsCache.expire(cerrors.id, errorsCache.defaultExpireTime);
  }

  this.status = 400;
  this.body = JSON.stringify({links: links, errors: cerrors});
  return yield next;
};

ctrl.deleteAuthorization = function *(next) {
  var links = respData.createSimpleLinks(this.path);
  var errors = validators.check(this, [{
    'token': [{
      checkFun: 'need',
      errStatus: 400,
      errCode: '01002',
      errDetail: '缺少token参数',
      errSource: 'deleteAuthorization'
    }]
  }]);
  if (errors) {
    log(errors.source, errors.detail);
    var setErrorRes = yield errorsCache.set(errors.id, JSON.stringify(errors));
    if (setErrorRes === 1) {
      setErrorRes = errorsCache.expire(errors.id, errorsCache.defaultExpireTime);
    }
    this.status = errors.status;
    this.body = JSON.stringify({links: links, errors: errors});
    return yield next;
  }
  // delete token
  var deleteTokenRes = yield tokenCache.del(this.params.token);
  this.status = 200;
  links = respData.createSimpleLinks(this.path, 'index');
  this.body = JSON.stringify({links: links});
  return yield next;
};

ctrl.createAuthByKey = function *(next) {
  var links = respData.createSimpleLinks(this.path);
  var errors = validators.check(this, [{
    'token': [{
      checkFun: 'need',
      errStatus: 401,
      errCode: '01001',
      errDetail: '缺少表单token参数',
      errSource: 'createAuthorization'
    }]
  }, {
    'key': [{
      checkFun: 'need',
      errStatus: 400,
      errCode: '01004',
      errDetail: '缺少key参数',
      errSource: 'deleteAuthorization'
    }]
  }]);
  if (errors) {
    log(errors.source, errors.detail);
    var setErrorRes = yield errorsCache.set(errors.id, JSON.stringify(errors));
    if (setErrorRes === 1) {
      setErrorRes = errorsCache.expire(errors.id, errorsCache.defaultExpireTime);
    }
    this.status = errors.status;
    this.body = JSON.stringify({links: links, errors: errors});
    return yield next;
  }
  // check token in redis
  var getTokenRes = yield tokenCache.get(this.request.body.token);
  if (getTokenRes === null || getTokenRes !== this.request.body.token) {
    var checkTokenErrors = respData.createErrors(
      401,
      '01001',
      'token不存在或者页面超时',
      config.constants.formToken,
      {
        method: this.request.method,
        path: this.path
      }
    );
    var setTERes = yield errorsCache.set(checkTokenErrors.id, JSON.stringify(checkTokenErrors));
    if (setTERes === 1) {
      setTERes = errorsCache.expire(checkTokenErrors.id, errorsCache.defaultExpireTime);
    }

    this.status = 401;
    this.body = JSON.stringify({links: links, errors: checkTokenErrors});
    return yield next;
  }
  var ip = tools.getClientIp(this.request);
  var date = new Date();
  // var params = this.params;
  var phoneNumErrors = validators.check(this, [{
    // param: 'token',
    'num': [{
      checkFun: 'isMobil',
      errStatus: 400,
      errCode: '01002',
      errDetail: '号码错误',
      errSource: 'createAuthByKey'
    }]
  }]);
  if (!phoneNumErrors) {
    var num = this.params.num;
    var getLimitRes = yield limitCache.get(num);
    log('phone num: ', num, ' codes time: ', getLimitRes);
    if (getLimitRes !== null || getLimitRes > config.codesLimit) {
      // over limit
      var limitErrors = respData.createErrors(
        400,
        '00003',
        '超过了发送验证码限制次数',
        'createAuthByKey',
        {
          method: this.request.method,
          path: this.path
        }
      );
      var setLERes = yield errorsCache.set(limitErrors.id, JSON.stringify(limitErrors));
      if (setLERes === 1) {
        setLERes = errorsCache.expire(limitErrors.id, errorsCache.defaultExpireTime);
      }
      this.status = 400;
      links = respData.createSimpleLinks(this.path, 'index');
      this.body = JSON.stringify({links: links, errors: limitErrors});
      return yield next;
    }
    var setLimitRes = yield limitCache.set(num, getLimitRes + 1);
    // log('set phone: ', num, 'limit: '. getLimitRes + 1, 'res: ', setLimitRes);
    var getTokenRes = yield tokenCache.get(num);
    if (getTokenRes !== null) {
      this.status = 200;
      var phoneNumEntity = JSON.parse(getTokenRes);
      links = respData.createSimpleLinks(this.path, 'index');
      var datas = respData.createSimpleData(config.constants.formToken, phoneNumEntity.token);
      this.body = JSON.stringify({links: links, data: datas});
      return yield next;
    }
    var token = koajwt.sign({
      num: num,
      timestamp: Date.now(),
      type: config.constants.authorization
    }, config.secret);
    var codes = tools.getCodes();
    // send codes to user phone
    var codesResp = yield httpRequest.sendCodes(num, date);
    log('send codes resp: ',
      codesResp.result.respCode,
      myErrCode.codes[codesResp.result.respCode]
    );
    if (codesResp.result.respCode === '00000') {
      var setCodesRes = yield tokenCache.set(num, JSON.stringify({
        ip: ip,
        token: token,
        createTime: date.toString(),
        type: config.constants.authorization,
        codes: codes
      }));
      if (setCodesRes === 1) {
        setCodesRes = yield tokenCache.expire(token, tokenCache.defaultExpireTime);
      }
      this.status = 201;
      links = respData.createSimpleLinks(this.path, 'index');
      var sendCodesdatas = respData.createSimpleData(config.constants.formToken, token);
      this.body = JSON.stringify({links: links, data: sendCodesdatas});
      return yield next;
    }
    var cerrors = respData.createErrors(
      500,
      '00002',
      myErrCode.codes[codesResp.result.respCode],
      'createAuthByKey',
      {
        method: this.request.method,
        path: this.path
      }
    );
    var setCERes = yield errorsCache.set(cerrors.id, JSON.stringify(cerrors));
    if (setCERes === 1) {
      setCERes = errorsCache.expire(cerrors.id, errorsCache.defaultExpireTime);
    }
    this.status = 500;
    links = respData.createSimpleLinks(this.path, 'index');
    this.body = JSON.stringify({links: links, errors: errors});
  }
  log(phoneNumErrors.source, phoneNumErrors.detail);
  return yield next;
};

/**
 * [*checkUserAndgetToken description]
 * @param {Function} next          [description]
 * @yield {[type]}   [description]
 */
ctrl.checkUserAndgetToken = function *(next) {
  var query = this.query;
  log(query);
  if (!query) {
    this.status = 400;
    this.body = {message: 'miss params'};
  }
  if (!query.userName) {
    this.status = 400;
    this.body = {message: 'miss userName'};
    return;
  }
  if (!query.passWord) {
    this.status = 400;
    this.body = {message: 'miss passWord'};
    return;
  }
  // this.log.info('user login infomation: ', query);
  var user = yield service.checkUserLogin(query);
  // log(user.roleId);
  // this.log.info('query user: ', user);
  if (user) {
    // var role = yield roleService.findByIdWithMenuOperator(user.roleId);
    // log(role.purview[0].menu.name);
    // this.log.info('query user and role: ', user, role);
    // user.passWord = 'fejdfd*^^%&we32323212fsfa>:{P';
    var token = koajwt.sign({
      userId: user._id,
      userName: user.userName,
      roleId: user.roleId._id
    }, config.secret);
    var res = yield tokenCache.set(token, '1');
    if (res === 1) {
      res = yield tokenCache.expire(token, tokenCache.defaultExpireTime);
    }
    // user.update(user, {accessToken: token});
    // yield service.updateAccessToken(user, token);
    user.passWord = '';
    user.accessToken = token;
    this.status = 200;
    this.body = user;
  } else {
    this.status = 401;
    this.body = {message: 'can not find user, please go back'};
  }
  yield next;
};

ctrl.register = function *(next) {
  var data = this.request.body;
  if (!data) {
    this.status = 400;
    this.body = {message: 'miss params'};
  }
  if (!data.userName) {
    this.status = 400;
    this.body = {message: 'miss userName'};
    return;
  }
  if (!data.passWord) {
    this.status = 400;
    this.body = {message: 'miss passWord'};
    return;
  }
  var role = yield roleService.defaultRole();
  if (role) {
    data.roleId = role._id;
  }
  var user = yield service.createUser(data);
  if (user) {
    this.status = 201;
    this.state.updatedAt = user.updatedAt.toUTCString();
    this.state.id = user._id;
    this.body = user;
  } else {
    this.status = 412;
    this.body = {message: 'could not create entity'};
  }
  yield next;
};

ctrl.checkUserName = function *(next) {
  var query = this.query;
  if (!query) {
    this.status = 400;
    this.body = {message: 'miss params'};
  }
  if (!query.userName) {
    this.status = 400;
    this.body = {message: 'miss userName'};
    return;
  }
  var user = yield service.findByUserName(query);
  if (user) {
    this.status = 412;
    this.body = {message: 'user name is exist'};
  } else {
    this.status = 200;
    this.body = {message: 'user name is not used'};
  }
  yield next;
};

ctrl.find = function *(next) {
  // query data
  // var token = tools.getAccessToken(this.header);
  var query = this.request.query;
  log(query, query.search);
  query = JSON.parse(query);
  if (tools.isEmptyObject(query)) {
    this.status = 400;
    this.body = {message: 'miss params'};
  } else {
    var options = {};
    if (query.limit) {
      options.limit = query.limit;
    }
    if (query.offset) {
      options.offset = query.offset;
    }
    // log(options);
    var users = yield service.findUserList(query.search);
    // log(users);
    if (users && users.length > 0) {
      this.status = 200;
      this.body = users;
    } else {
      this.status = 204;
      this.body = {message: 'not found'};
    }
  }
  yield next;
};

ctrl.findById = function *(next) {
  // query data
  var id = this.params.id;
  if (id.length !== 24) {
    this.status = 400;
    this.body = {message: 'the id is error'};
  } else {
    var user = yield service.findUserById(id);
    if (user) {
      this.status = 200;
      this.body = user;
    } else {
      this.status = 204;
      this.body = {message: 'not found'};
    }
  }
  yield next;
};

ctrl.create = function *(next) {
  // post data
  var data = this.request.body;
  var token = tools.getAccessToken(this.header);
  data.updatedBy = token.userId;
  // register
  if (!data.roleId) {
    var role = yield roleService.defaultRole();
    if (role) {
      data.roleId = role._id;
    }
  }
  var user = yield service.createUser(data);
  if (user) {
    this.status = 201;
    this.body = user;
    this.state.updatedAt = user.updatedAt.toUTCString();
    this.state.id = user._id;
  } else {
    this.status = 412;
    this.body = {message: 'could not create entity'};
  }
  yield next;
};

ctrl.updateById = function *(next) {
  //query data
  var data = this.request.body;
  var modify = {$set: data};
  var user = yield service.findByIdAndUpdate(this.params.id, modify);
  if (user) {
    this.status = 200;
    this.body = user;
    this.state.updatedAt = user.updatedAt.toUTCString();
    this.state.id = user._id;
  } else {
    this.status = 412;
    this.body = {message: 'could not find an entity'};
  }
  yield next;
};

ctrl.updateRoleByUserId = function *(next) {
  //query data
  var data = this.request.body;
  var modify = {};
  // PUT /:id/roles/:rid
  modify.$set = {roleId: this.params.rid};
  // modify.$set.roleId = this.params.rid;
  var user = yield service.findByIdAndUpdate(this.params.id, modify);
  if (user) {
    this.status = 200;
    this.body = {};
    this.state.updatedAt = user.updatedAt.toUTCString();
    this.state.id = user._id;
  } else {
    this.status = 412;
    this.body = {message: 'could not find an entity'};
  }
  yield next;
};

ctrl.delete = function *(next) {
  //query data
  var data = this.request.body;
  var result = yield service.remove(data);
  if (result) {
    this.status = 200;
    this.body = result;
  } else {
    this.status = 412;
    this.body = {message: 'could not find an entity'};
  }
  yield next;
};

ctrl.deleteById = function *(next) {
  //query data
  var data = this.request.body;
  var result = yield service.findByIdAndRemove(this.params.id);
  if (result) {
    this.status = 200;
    this.body = result;
  } else {
    this.status = 412;
    this.body = {message: 'could not find an entity'};
  }
  yield next;
};
